#include "ExpAST.h"
#include "Globe.h"
#include <iostream>
#include "parser.h"
#include "Globe.h"
#pragma warning(disable:4267)
using std::cout;
using std::endl;

unique_ptr<BaseExpAST> parser::parseUnaryExp()
{
    unique_ptr<UnaryAST>ret;
    switch (this->lexer->next_token())
    {
    case TOK::PLUS:
        this->lexer->match(TOK::PLUS);
        ret.reset(new UnaryAST);
        ret->op = UnaryAST::POS;
        ret->content = this->parseUnaryExp();
        break;
    case TOK::MINUS:
        this->lexer->match(TOK::MINUS);
        ret.reset(new UnaryAST);
        ret->op = UnaryAST::NEG;
        ret->content = this->parseUnaryExp();
        break;
    case TOK::NOT:
        this->lexer->match(TOK::NOT);
        ret.reset(new UnaryAST);
        ret->op = UnaryAST::NOT;
        ret->content = this->parseUnaryExp();
        break;
    case TOK::INCR:
        this->lexer->match(TOK::INCR);
        ret.reset(new UnaryAST);
        ret->op = UnaryAST::INCR;
        ret->content = this->parseUnaryExp();
        if (!ret->content->is_lval())
        {
            std::cerr << "lvalue required as increment operand" << std::endl;
            exit(-1);
        }
        break;
    case TOK::DECR:
        this->lexer->match(TOK::DECR);
        ret.reset(new UnaryAST);
        ret->op = UnaryAST::DECR;
        ret->content = this->parseUnaryExp();
        if (!ret->content->is_lval())
        {
            std::cerr << "lvalue required as decrement operand" << std::endl;
            exit(-1);
        }
        break;
    case TOK::BNOT:
        this->lexer->match(TOK::BNOT);
        ret.reset(new UnaryAST);
        ret->op = UnaryAST::BNOT;
        ret->content = this->parseUnaryExp();
        break;
    default:
        return this->parsePrimaryExp();
    }
    return ret;
}

void UnaryAST::Dump() const
{
    cout << "{\"type\":\"Unary\",\"op\":\"";
    switch (this->op)
    {
    case POS:
        cout << "+";
        break;
    case NEG:
        cout << "-";
        break;
    case NOT:
        cout << "!";
    case INCR:
        cout << "++";
        break;
    case DECR:
        cout << "--";
        break;
    case BNOT:
        cout << "~";
        break;
    default:break;
    }
    cout << "\",\"content\":";
    this->content->Dump();
    cout << "}";
}

bool UnaryAST::is_lval() const
{
    switch (this->op)
    {
    case UnaryAST::BNOT:
    case UnaryAST::NEG:
    case UnaryAST::NOT:
    case UnaryAST::POS:
        return false;
    case UnaryAST::DECR:
    case UnaryAST::INCR:
        return true;
    default:
        return this->content->is_lval();
    }
}

void UnaryAST::Analyze() const
{
    this->content->Analyze();
    switch (this->op)
    {
    case UnaryAST::NEG:

        GENASM(cmd_code::MOV, CVR(EAX), CVR(EDX));
        GENASM(cmd_code::MOV, cmd_val(cmd_val::INTEGER, 0, 0), CVR(EAX));
        GENASM(cmd_code::SUB, CVR(EAX), CVR(EDX));
        break;
    case UnaryAST::NOT:
        GENASM(cmd_code::MOV, CVI(0), CVR(EDX));
        GENASM(cmd_code::CMP, CVR(EAX), CVR(EDX));
        GENASM(cmd_code::SETE, CVR(AL), cmd_val());
        GENASM(cmd_code::MOVZBL, CVR(AL), CVR(EAX));
        break;
    case UnaryAST::INCR:
        GENASM(cmd_code::ADD, CVR(EAX), CVI(1));
        GENASM(cmd_code::MOV, CVR(EAX), this->content->get_address());
        break;
    case UnaryAST::DECR:
        GENASM(cmd_code::SUB, CVR(EAX), CVI(1));
        GENASM(cmd_code::MOV, CVR(EAX), this->content->get_address());
        break;
    case UnaryAST::BNOT:
        GENASM(cmd_code::NOT, CVR(EAX), cmd_val());
        break;
    default:
        break;
    }
}
